package app

import (
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"strconv"
	"tapay-ws/global"
	"tapay-ws/pkg/convert"
	"time"
)

type Claims struct {
	UserID   uint32 `json:"user_id"`
	UserRole string `json:"user_role"`
	Nickname string `json:"nickname"`
	jwt.StandardClaims
}

func GetJWTSecret() []byte {
	return []byte(global.JWTSetting.Secret)
}

func GenerateToken(userID uint32, userRole, nickname string, isLogin bool) (string, error) {
	return GenerateTokenWithExpire(userID, userRole, nickname, global.JWTSetting.Expire, isLogin)
}

func GenerateTokenWithExpire(userID uint32, userRole, nickname string, expire time.Duration, isLogin bool) (string, error) {
	if expire < 86400000000000 && isLogin {
		expire = 86400000000000
	}
	nowTime := time.Now()
	expireTime := nowTime.Add(expire).Unix()
	global.Logger.Infof(context.Background(), fmt.Sprintf("uid:%d;expireTime:%d;expire:%d", userID, expireTime, expire))
	claims := Claims{
		UserID:   userID,
		UserRole: userRole,
		Nickname: nickname,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime,
			Issuer:    global.JWTSetting.Issuer,
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(GetJWTSecret())
	return token, err
}

func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return GetJWTSecret(), nil
	})
	if err != nil {
		return nil, err
	}
	if tokenClaims != nil {
		claims, ok := tokenClaims.Claims.(*Claims)
		if ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}

func ParseTokenWithoutValid(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return GetJWTSecret(), nil
	})
	if tokenClaims != nil && tokenClaims.Claims != nil {
		claims, ok := tokenClaims.Claims.(*Claims)
		if ok {
			return claims, nil
		}
	}

	return nil, err
}

func GetUserId(c *gin.Context) uint32 {
	userId, exists := c.Get("userID")
	if exists {
		return userId.(uint32)
	}
	return 0
}

func GetUserType(c *gin.Context) uint8 {
	userType := c.GetString("userRole")
	if userType == "" {
		return 0
	}
	return convert.StrTo(userType).MustUInt8()
}

func GetUserAddress(c *gin.Context) string {
	address, exists := c.Get("address")
	if exists {
		v := address.(string)
		return v
	}
	return ""
}

func GetUserIdStr(c *gin.Context) string {
	userId, exists := c.Get("userID")
	if exists {
		u := userId.(uint32)
		return strconv.Itoa(int(u))
	}
	return ""
}
